package io.gitee.zhangbinhub.admin.oauth.service

import com.mybatisflex.core.query.QueryWrapper
import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.admin.oauth.base.OauthBaseService
import io.gitee.zhangbinhub.admin.oauth.entity.Organization
import io.gitee.zhangbinhub.admin.oauth.entity.User
import io.gitee.zhangbinhub.admin.oauth.entity.UserOrg
import io.gitee.zhangbinhub.admin.oauth.entity.UserOrgMng
import io.gitee.zhangbinhub.admin.oauth.po.OrganizationPo
import io.gitee.zhangbinhub.admin.oauth.repo.OrganizationRepository
import io.gitee.zhangbinhub.admin.oauth.repo.UserOrgMngRepository
import io.gitee.zhangbinhub.admin.oauth.repo.UserOrgRepository
import io.gitee.zhangbinhub.admin.oauth.repo.UserRepository
import io.gitee.zhangbinhub.admin.resource.server.vo.OrganizationVo
import org.noear.solon.annotation.Component
import org.noear.solon.annotation.Inject
import org.noear.solon.data.annotation.Transaction

@Component
open class OrganizationService(
    @Inject userRepository: UserRepository,
    private val organizationRepository: OrganizationRepository,
    private val userOrgRepository: UserOrgRepository,
    private val userOrgMngRepository: UserOrgMngRepository,
) : OauthBaseService(userRepository) {

    fun getAllOrgList(): MutableList<Organization> =
        organizationRepository.selectListByQuery(QueryWrapper.create().orderBy(Organization::sort).asc())

    fun getOrgList(): MutableList<Organization> = organizationRepository.selectAll().let {
        val map: MutableMap<String, Organization> = mutableMapOf()
        it.forEach { item ->
            map[item.id] = item
        }
        sortOrganizationList(formatToTreeList(map))
    }

    private fun sortOrganizationList(organizationList: MutableList<Organization>): MutableList<Organization> =
        organizationList.let { list ->
            list.forEach { organization ->
                if (organization.children.isNotEmpty()) {
                    sortOrganizationList(organization.children)
                }
            }
            organizationList.apply {
                this.sortBy { it.sort }
            }
        }

    @Throws(WebException::class)
    private fun doSave(userInfo: User, organization: Organization, organizationPo: OrganizationPo): Organization =
        (if (organizationPo.userIds.isNotEmpty()) {
            userRepository.selectListByIds(organizationPo.userIds)
        } else {
            mutableListOf()
        }).let { userSetPo ->
            if (validateModifyUserSet(userInfo, organization.userSet, userSetPo)) {
                organization.copy(
                    name = organizationPo.name!!,
                    code = organizationPo.code!!,
                    area = organizationPo.area!!,
                    sort = organizationPo.sort
                ).apply {
                    parentId = organizationPo.parentId!!
                }.also {
                    organizationRepository.insertOrUpdate(it)
                }.also {
                    userOrgRepository.deleteByQuery(QueryWrapper.create().where(UserOrg::organizationId).eq(it.id))
                    if (organizationPo.userIds.isNotEmpty()) {
                        userOrgRepository.insertBatch(organizationPo.userIds.map { id ->
                            UserOrg(userId = id, organizationId = it.id)
                        })
                    }
                }
            } else {
                throw WebException("不合法的操作，不允许修改更高级别的用户列表！")
            }
        }

    /**
     * 是否有编辑权限
     *
     * @param user   当前登录用户
     * @param orgIds 机构ID
     * @return true|false
     */
    private fun isNotPermit(user: User, vararg orgIds: String): Boolean =
        !isSuper(user) && !getAllOrgList(organizationRepository, user.organizationMngSet.toMutableList()).map { it.id }
            .toMutableList().containsAll(mutableListOf(*orgIds))

    @Transaction
    @Throws(WebException::class)
    open fun doCreate(loginNo: String, organizationPo: OrganizationPo): Organization =
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            if (isNotPermit(userInfo, organizationPo.parentId!!)) {
                throw WebException("没有权限做此操作，请联系系统管理员")
            }
            return Organization().let {
                doSave(userInfo, it, organizationPo).apply {
                    userOrgMngRepository.insert(UserOrgMng(userId = userInfo.id, organizationId = this.id))
                }
            }
        } ?: throw WebException("无法获取当前用户信息")

    @Transaction
    @Throws(WebException::class)
    open fun doDelete(loginNo: String, idList: MutableList<String>) {
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            if (isNotPermit(userInfo, *idList.toTypedArray())) {
                throw WebException("没有权限做此操作，请联系系统管理员")
            }
            organizationRepository.selectListByQuery(QueryWrapper.create().where(Organization::getParentId).`in`(idList))
                .apply {
                    if (this.isNotEmpty()) {
                        throw WebException("存在下级机构，不允许删除")
                    }
                }
            userOrgRepository.deleteByQuery(QueryWrapper.create().where(UserOrg::organizationId).`in`(idList))
            userOrgMngRepository.deleteByQuery(QueryWrapper.create().where(UserOrgMng::organizationId).`in`(idList))
            organizationRepository.deleteBatchByIds(idList)
        } ?: throw WebException("无法获取当前用户信息")
    }

    @Transaction
    @Throws(WebException::class)
    open fun doUpdate(loginNo: String, organizationPo: OrganizationPo): Organization =
        getUserInfoByLoginNo(loginNo)?.let { userInfo ->
            val organization = organizationRepository.selectOneWithRelationsById(organizationPo.id!!)
            if (isNotPermit(userInfo, organization.id)) {
                throw WebException("没有权限做此操作，请联系系统管理员")
            }
            doSave(userInfo, organization, organizationPo)
        } ?: throw WebException("无法获取当前用户信息")

    @Throws(WebException::class)
    fun getModOrgList(loginNo: String): MutableList<Organization> =
        (getUserInfoByLoginNo(loginNo) ?: throw WebException("无法获取当前用户信息")).organizationMngSet.toMutableList()

    @Throws(WebException::class)
    fun getCurrAndAllChildrenForOrg(loginNo: String): MutableList<Organization> = getUserInfoByLoginNo(loginNo)?.let {
        getAllOrgList(organizationRepository, it.organizationSet.toMutableList())
    } ?: throw WebException("无法获取当前用户信息")

    @Throws(WebException::class)
    fun getCurrAndAllChildrenForMngOrg(loginNo: String): MutableList<Organization> =
        getUserInfoByLoginNo(loginNo)?.let {
            getAllOrgList(organizationRepository, it.organizationMngSet.toMutableList())
        } ?: throw WebException("无法获取当前用户信息")

    @Throws(WebException::class)
    fun getCurrAndAllChildrenForAllOrg(loginNo: String): MutableList<Organization> =
        getUserInfoByLoginNo(loginNo)?.let { user ->
            mutableListOf<Organization>().let { list ->
                list.addAll(user.organizationSet.toMutableList())
                list.addAll(user.organizationMngSet.toMutableList())
                getAllOrgList(organizationRepository, list)
            }
        } ?: throw WebException("无法获取当前用户信息")

    @Throws(WebException::class)
    fun getOrgInfo(orgId: String): OrganizationVo =
        organizationRepository.selectOneWithRelationsById(orgId).let { item ->
            OrganizationVo(
                id = item.id,
                code = item.code,
                name = item.name,
                parentId = item.parentId,
                area = item.area,
                sort = item.sort,
                userIds = item.userSet.map { it.id }.toMutableList()
            )
        }

    @Throws(WebException::class)
    fun getOrgInfoByCodeOrName(codeOrName: String): MutableList<OrganizationVo> =
        mutableListOf<OrganizationVo>().apply {
            organizationRepository.selectListByQuery(
                QueryWrapper.create()
                    .where(Organization::code).like(codeOrName)
                    .or(Organization::name).like(codeOrName)
            ).forEach { item ->
                this.add(
                    OrganizationVo(
                        id = item.id,
                        code = item.code,
                        name = item.name,
                        parentId = item.parentId,
                        area = item.area,
                        sort = item.sort
                    )
                )
            }
        }
}
